/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.flopbox.client;

import com.flopbox.server.FileInfo;
import com.flopbox.server.RemoteFlopboxServer;
import java.io.File;
import java.io.FileInputStream;
import java.nio.channels.FileLock;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardWatchEventKinds.*;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.rmi.Naming;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class FlopBoxClient implements FolderListener{

     public static String HOST = "158.38.43.32";
//    public static String HOST = "localhost";
    public static String NAME = "flopBoxServer";
    public static int PORT = 1337;
    private Watcher watcher;
    private RemoteFlopboxServer stub;
    private final static Logger LOGGER = Logger.getLogger(FlopBoxClient.class .getName());

    public FlopBoxClient() throws Exception {
        System.setProperty("java.rmi.server.hostname", HOST);
            
        // Try to set up connection to Server
        final String bindName = "rmi://" + HOST + ':' + PORT + '/' + NAME;

        stub = (RemoteFlopboxServer) Naming.lookup(bindName);
        System.out.println("Connected to server");

        // register directory and process its events
        Path dir = Paths.get(System.getProperty("user.home") + File.separator + "flopBox");

        // Check if path exists
        if (!dir.toFile().isDirectory()) {
            // Create path
            File f = dir.toFile();
            f.mkdir();
        }

        // Create watcher object
        watcher = new Watcher(dir);

        
        // Add listener to the folder watcher and run it.
        watcher.addFolderListener(this);
    }

    @Override
    public void fileChanged(Kind kind, Path child) {
        if (kind == ENTRY_CREATE) {
                uploadFile(child);
            }
    }
    
    private void uploadFile(Path child){
        try{
            File f = new File(child.toString());
            if(!f.getName().equalsIgnoreCase(".DS_Store")){
                stub.sendString(child.toString());
                if (isFileReady(f)) {
                    System.out.println("Can write");
                    int id = stub.initUpload(new FileInfo(new Date(),child.getFileName().toString()));

                    System.out.println("ID returned: " + id);
                    if (id > 0) {
                        FileInputStream fis = new FileInputStream(child.toFile());
                        byte[] buf = new byte[1024];
                        long start = System.currentTimeMillis();
                        long stop;
                        long counter = 0;
                        double seconds = 1;
                        while (isFileReady(f) && fis.read(buf) != -1) {                        
                            stub.uploadFile(id, buf);
                            counter += buf.length;
                            stop = System.currentTimeMillis();
                            seconds = (stop-start)/1000;
                            System.out.println("Speed: " + (counter/1024)/(seconds == 0 ? 1 : seconds));
                        }
                        System.out.println("File uploaded");
                        stub.doneUploadingFile(id);
                        printDirectory();
                    }

                    id = stub.initUpload(new FileInfo(new Date(),child.getFileName().toString()));

                    System.out.println("ID returned: " + id);
                    if (id > 0) {
                        FileInputStream fis = new FileInputStream(child.toFile());
                        byte[] buf = new byte[1024];

                        while (isFileReady(f) && fis.read(buf) != -1) {                        
                            stub.uploadFile(id, buf);
                        }
                    }
                }
            }
        }
        catch(Exception e){
            e.printStackTrace();
        }
        
    }
    
    private boolean isFileReady(File f) {
        int sleepTime = 1000;
        while (!canOpenFile(f)) {
            try {
                Thread.sleep(sleepTime);
                sleepTime *= 2;
            } catch (InterruptedException ex) {
                Logger.getLogger(FlopBoxClient.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return true;
    }

    private boolean canOpenFile(File f) {
        boolean canOpen = false;
        try (FileInputStream fin = new FileInputStream(f)){            
            canOpen = true;
        } catch (Exception e) {
            canOpen = false;
        }
        return canOpen;
    }

    public void startFolderWatcher() {
        watcher.start();
    }

    public void stopFolderWatcher() {
        if (watcher.isAlive()) {
            try {
                watcher.join();


            } catch (InterruptedException ex) {
                Logger.getLogger(FlopBoxClient.class
                        .getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
    public void startServerSync(){
        
    }
    private void printDirectory(){
        ArrayList<FileInfo> metaFiles=watcher.listFiles();
        System.out.println("Current files in "+watcher.getDir()+":");
        for(FileInfo fi:metaFiles){
            System.out.println(fi.toString());
        }
    }
    private void refresh(){
        try{
            ArrayList<FileInfo> myFiles=watcher.listFiles();
            //Retrives a list of modified files from server
            List<FileInfo> serverFiles=stub.getFilesModified(myFiles);
            for(FileInfo sf:serverFiles){
                //Checks if the file exists on client side
                int index=java.util.Collections.binarySearch(myFiles, sf);
                if(index>=0){
                    FileInfo myFile=myFiles.get(index);
                    
                    //Constructs the filesystem spesific Path to local file:
                    Path path=FileSystems.getDefault().getPath(watcher.getDir(), myFile.getFilename());
                    
                    //If modified date for the two InfoFile objects are equal the client file needs to be commited
                    if(myFile.getModified().equals(sf.getModified())){
                        //Upload file myFile
                        uploadFile(path);
                    }
                    else{
                        //Checks if the file is deleted on server
                        if(sf.isDeleted()){
                            //Delete file on client side
                            delete(path);
                        }
                        //Updates file on client side
                        else{
                            //Update file on client side
                            //Trigger update on server...
                        }
                    }
                }
                else{
                    
                }
            }
            
        }
        catch(Exception e){
            log("refresh(): Could not refresh fileList");
        }
    }
    
    //Functino for deleting files
    private void delete(Path path){
        String stringPath=path.toString();
        File f=new File(stringPath);
        if(f.delete()){
            log("delete(): File "+stringPath+" deleted");
        }
        else{
            log("delete(): File "+stringPath+" could not be deleted");
        }
    }
    
    //Funciton for logging
    private void log(String message){
        LOGGER.log(Level.INFO, message);
    }
    

    public static void main(String[] args) {
        FlopBoxClient fbc = null;
        try {
            fbc = new FlopBoxClient();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (fbc != null) {
            fbc.startFolderWatcher();
        }
    }

    
}
